package org.netwia.networks;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.netwia.utils.ValidationUtils;

/**
 * Package which is possible to send to he remote place.
 * 
 * @author radek.hecl
 *
 * @param <T> network parameter type
 */
public class RemotePackage<T> {

    /**
     * Builder object.
     *
     * @param <T> network parameter type
     */
    public static class Builder<T> {

        /**
         * Name of the client who is sending the message.
         */
        private String senderClient;
        
        /**
         * Name of the client where the message goes. * for broadcast to all the other clients, ? for selecting one randomly.
         */
        private String targetClient;
        
        /**
         * Name of the package to send.
         */
        private String packageName;

        /**
         * Data to be sent.
         */
        private T data;
        
        /**
         * Sets name of the client who is sending the message.
         * 
         * @param senderClient name of the client who is sending the message
         * @return this instance
         */
        public Builder<T> setSenderClient(String senderClient) {
            this.senderClient = senderClient;
            return this;
        }
        
        /**
         * Sets target client. This is where the message goes. * for broadcast to all the other clients, ? for selecting one randomly.
         * 
         * @param targetClient target client, * for broadcast
         * @return this instance
         */
        public Builder<T> setTargetClient(String targetClient) {
            this.targetClient = targetClient;
            return this;
        }
        
        /**
         * Sets name of the package to query for.
         * 
         * @param packageName name of the package to query for
         * @return this instance
         */
        public Builder<T> setPackageName(String packageName) {
            this.packageName = packageName;
            return this;
        }

        /**
         * Sets data to be sent.
         * 
         * @param data data to be sent
         * @return this instance
         */
        public Builder<T> setData(T data) {
            this.data = data;
            return this;
        }
        
        /**
         * Builds the result object.
         * 
         * @return created object
         */
        public RemotePackage<T> build() {
            return new RemotePackage<T>(this);
        }
    }

    /**
     * Name of the client who is sending the message.
     */
    private String senderClient;

    /**
     * Name of the client where the message goes. * for broadcast to all the other clients, ? for selecting one randomly.
     */
    private String targetClient;
    
    /**
     * Name of the package to send.
     */
    private String packageName;

    /**
     * Data to be sent.
     */
    private T data;

    /**
     * Creates new instance.
     * 
     * @param builder builder object
     */
    public RemotePackage(Builder<T> builder) {
        senderClient = builder.senderClient;
        targetClient = builder.targetClient;
        packageName = builder.packageName;
        data = builder.data;
        guardInvariants();
    }

    /**
     * Guards this object to be consistent. Throws exception if this is not the case.
     */
    private void guardInvariants() {
        ValidationUtils.guardNotEmpty(senderClient, "senderClient cannot be empty");
        ValidationUtils.guardNotEmpty(targetClient, "targetClient cannot be empty");
        ValidationUtils.guardNotEmpty(packageName, "packageName cannot be empty");
        ValidationUtils.guardNotNull(data, "data cannot be null");
    }
    
    /**
     * Returns name of the client who is sending the message.
     * 
     * @return name of the client who is sending the message
     */
    public String getSenderClient() {
        return senderClient;
    }
    
    /**
     * Returns client identification where data are sent. Returns * if all connected clients
     * are targets. Returns ? if one randomly selected client is the target.
     * 
     * @return client identification where data are sent 
     */
    public String getTargetClient() {
        return targetClient;
    }
    
    /**
     * Returns package name.
     * 
     * @return package name
     */
    public String getPackageName() {
        return packageName;
    }
    
    /**
     * Returns data.
     * 
     * @return data
     */
    public T getData() {
        return data;
    }

    @Override
    public int hashCode() {
        return HashCodeBuilder.reflectionHashCode(this);
    }

    @Override
    public boolean equals(Object obj) {
        return EqualsBuilder.reflectionEquals(this, obj);
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }

    /**
     * Shortcut for package creation
     * 
     * @param senderClient sender client
     * @param targetClient target client
     * @param packageName name of the package
     * @param data data
     * @return created package
     */
    public static <T> RemotePackage<T> create(String senderClient, String targetClient, String packageName, T data) {
        return new RemotePackage.Builder<T>().
                setSenderClient(senderClient).
                setTargetClient(targetClient).
                setPackageName(packageName).
                setData(data).
                build();
    }

}
